#pragma once
#include <iostream>
#include <unistd.h>
#include <vector>
#include <sstream>
#include <unordered_map>
#include <sys/stat.h>
#include <sys/sendfile.h>
#include <fcntl.h>
#include <algorithm>
#include <sys/wait.h>
#include "Util.hpp"

#define SEP ": "
#define OK 200
#define NOT_FOUND 404
#define BAD_REQUEST 400
#define SERVER_ERROR 500

#define WROOT "wwwroot"
#define HOME_PAGE "index.html"
#define PAGE_404 "404.html"
#define HTTP_VERSION "HTTP/1.0"
#define LINE_END "\r\n"

//#define DEBUG

static std::string CodeDesc(int code)
{
    std::string desc;
    switch (code)
    {
    case 200:
        desc = "OK";
        break;
    case 404:
        desc = "Not Found";
        break;
    default: 
        break;
    }

    return desc;
}

static std::string SuffixDesc(const std::string suffix)
{
    static std::unordered_map<std::string, std::string> suffixdesc =
        {
            {".html", "text/html"},
            {".css", "text/css"},
            {".js", "application/json"},
            {".xml", "application/xml"},
            {".jpg", "image/jpeg"}
        };

    auto it = suffixdesc.find(suffix);
    if (it != suffixdesc.end())
    {
        return it->second;
    }

    return "text/html";
}

class HttpRequest
{
public:
    HttpRequest()
        :_content_length(0),
        _cgi(false)
    {}

    ~HttpRequest()
    {}

public:
    std::string _request_line;
    std::vector<std::string> _request_header;
    std::string _blank;
    std::string _request_body;

    // 解析完毕的结果
    std::string _method;
    std::string _url;
    std::string _path;          // 请求目录和参数
    std::string _query_string;
    std::string _version;

    std::unordered_map<std::string, std::string> _header_kv;
    int _content_length;
    std::string _suffix;
    int _size;

    // CGI机制
    bool _cgi;
};

class HttpResponse
{
public:
    HttpResponse()
        :_blank(LINE_END),
         _status_code(OK),
         _fd(-1)
    {}

    ~HttpResponse()
    {}

public:
    std::string _status_line;
    std::vector<std::string> _response_header;
    std::string _blank;
    std::string __response_body;
    int _fd;

    int _status_code;
};

class EndPoint
{
public:
    bool RecvHttpRequestLine()
    {
        auto &line = _http_request._request_line;
        if (Util::ReadLine(_sock, line) > 0)
        {
            line.resize(line.size() - 1);
            LOG(INFO, _http_request._request_line);
        }
        else
        {
            _stop = true;
        }

        return _stop;
    }

    bool RecvHttpRequestHeader()
    {
        std::string line;
        while (true)
        {
            line.clear();
            if (Util::ReadLine(_sock, line) <= 0)
            {
                _stop = true;
                break;
            }

            if (line == "\n")
            {
                _http_request._blank = line;
                break;
            }

            line.resize(line.size() - 1);
            _http_request._request_header.push_back(line);
        }

        return _stop;
    }

    void ParseHttpRequestLine()
    {
        auto& line = _http_request._request_line;
        std::stringstream s(line);
        s >> _http_request._method >> _http_request._url >> _http_request._version;
        auto &method = _http_request._method;
        std::transform(method.begin(), method.end(), method.begin(), ::toupper);
    }

    void ParseHttpRequestHander()
    {
        for (auto& line: _http_request._request_header)
        {
            std::string key;
            std::string value;
            if (Util::CutString(line, key, value, SEP))
            {
                std::cout << key << ":" << value << std::endl;
                _http_request._header_kv.insert({key, value});
            }
        }
    }

    bool IsNeedRecvHttpRequestBody()
    {
        if (_http_request._method == "POST")
        {   
            auto it = _http_request._header_kv.find("Content-Length");
            if (it != _http_request._header_kv.end())
            {
                _http_request._content_length = atoi(it->second.c_str());
                return true;
            }
        }

        return false;
        
    }

    bool RecvHttpRequestBody()
    {
        if (IsNeedRecvHttpRequestBody())
        {
            int len = _http_request._content_length;
            auto &body = _http_request._request_body;
            char ch = 0;
            while (len)
            {
                ssize_t s = recv(_sock, &ch, 1, 0);
                if (s > 0)
                {
                    body.push_back(ch);
                    len--;
                }
                else
                {
                    _stop = true;
                    break;
                }
            }

        }

        return _stop;
    }

public:
    EndPoint(int sock)
        :_sock(sock),
         _stop(false)
    {}

    void RecvHttpRequest()
    {
        if (!RecvHttpRequestLine() && !RecvHttpRequestHeader())
        {
            ParseHttpRequestLine();
            ParseHttpRequestHander();
            RecvHttpRequestBody();
        }
    }

    // cgi处理
    int ProcessCgi()
    {
        int code = OK;
        auto &bin = _http_request._path;
        auto &method = _http_request._method;
        auto &query_string = _http_request._query_string; // GET
        auto &body_text = _http_request._request_body; // POST
        auto &response_body = _http_response.__response_body;
        int content_length = _http_request._content_length;

        // 环境变量，用来GET传
        std::string query_string_env;
        std::string method_env;
        std::string content_length_env;

        // 管道通信
        int input[2];
        int output[2];

        if (pipe(input) < 0)
        {
            LOG(ERROR, "pipe input error");
            code = SERVER_ERROR;
            return code;
        }

        if (pipe(output) < 0)
        {
            LOG(ERROR, "pipe input error");
            code = SERVER_ERROR;
            return code;
        }
        
        // 父进程关闭input写端，只读。关output读端，只写
        // 新线程，从头到尾只有一个进程，不能本进程替换, 创建新进程
        pid_t pid = fork();
        if (pid == 0)
        {
            // exec
            close(input[0]);
            close(output[1]);

            // 通过方法判断从哪取参数
            method_env = "METHOD=";
            method_env += method;
            putenv(((char*)method_env.c_str()));
            if (method == "GET")
            { 
                query_string_env += "QUERY_STRING=";
                query_string_env += query_string;
                // 注册环境变量
                putenv(((char*)query_string_env.c_str()));
            }
            else if (method == "POST")
            {
                content_length_env = "CONTENT_LENGTH=";
                content_length_env += std::to_string(content_length);
                putenv((char*)(content_length_env.c_str()));
            }
            else
            {
                // do nothing
            }
            std::cout << "替换的程序为:" << bin << std::endl;
            dup2(input[1], 1);
            dup2(output[0], 0);
            execl(bin.c_str(), bin.c_str(), nullptr);
            exit(1);
        }
        else if (pid > 0)
        {
            // parent
            close(input[1]);
            close(output[0]);

            if (method == "POST")
            {
                const char *start = body_text.c_str();
                int total = 0;
                int size = 0;
                // 防止一次没写完
                while ((total < content_length) && (size = write(output[1], start + total, body_text.size() - total)) > 0)
                {
                    total += size;
                }
            }

            // 读取cgi返回的数据
            char ch = 0;
            while (read(input[0], &ch, 1) > 0)
            {
                response_body.push_back(ch);
            }

            // 判断子进程退出状态
            int status = 0;
            pid_t ret = waitpid(pid, &status, 0);
            if (ret == pid)
            {
                if (WIFEXITED(status))
                {
                    if (WEXITSTATUS(status) == 0)
                    {
                        code = OK;
                    }
                    else
                    {
                        code = BAD_REQUEST;
                    }
                }
                else
                {
                    code = SERVER_ERROR;
                }
            }

            // 使用完后关闭描述符
            close(input[0]);
            close(output[1]);           
        }
        else
        {
            LOG(ERROR, "fork error");
            return 404;
        }

        return code;
    }

    int ProcessNoCgi()
    {
        _http_response._fd = open(_http_request._path.c_str(), O_RDONLY);
        if (_http_response._fd >= 0)
        {
            return OK;
        }
        return NOT_FOUND;
    }

    void BuildOkResponse()
    {
        std::string line = "Content-Type: ";
        line += SuffixDesc(_http_request._suffix);
        line += LINE_END;
        _http_response._response_header.push_back(line);

        line += "Content-Length: ";
        if (_http_request._cgi)
        {
            line += std::to_string(_http_response.__response_body.size());
        }
        else
        {
            // get
            line += std::to_string(_http_request._size);
        }
        line += LINE_END;
        _http_response._response_header.push_back(line);
    }

    void HandlerError(std::string page)
    {
        _http_request._cgi = false;
        // 给用户返回对应错误页面
        _http_response._fd = open(page.c_str(), O_RDONLY);
        if (_http_response._fd > 0)
        {
            struct stat st;
            stat(page.c_str(), &st);
            _http_request._size = st.st_size;
            std::string line = "Content-Type: text/html";  // 这里明确了是返回网页
            line += LINE_END;
            _http_response._response_header.push_back(line);

            line = "Content-Length: ";
            line += std::to_string(st.st_size);
            line += LINE_END;
            _http_response._response_header.push_back(line);
        }
    }

    void BuildHttpResponseHelper()
    {
        auto &code = _http_response._status_code;
        auto &status_line = _http_response._status_line;
        // 构建状态行
        status_line = HTTP_VERSION;
        status_line += " ";
        status_line += std::to_string(code);
        status_line += " ";
        status_line += CodeDesc(code);
        status_line += LINE_END;
        
        // 构建响应正文，可能包含报头
        std::string path = WROOT;
        path += "/";
        switch (code)
        {
        case OK:
            BuildOkResponse();
            break;
        case NOT_FOUND:
            path += PAGE_404;
            HandlerError(path);
            break;
        // case 500:
        //     HandlerError(PAGE_500);
        //     break;
        default:
            path += PAGE_404;
            HandlerError(path);
            break;
        }
    }

    void BuildHttpResponse()
    {
        struct stat st;
        int found = 0;
        std::string path;
        auto &code = _http_response._status_code;

        std::cout << "方法是: " << _http_request._method << std::endl;
        if (_http_request._method != "GET" && _http_request._method != "POST")
        {
            // 非法请求
            LOG(WARNING, "method not is right");
            code = BAD_REQUEST;
            goto END;
        }

        if (_http_request._method == "GET")
        {
            ssize_t pos = _http_request._url.find("?");
            if (pos != std::string::npos)
            {
                Util::CutString(_http_request._url, _http_request._path, _http_request._query_string, "?");
                _http_request._cgi = true;
            }
            else
            {
                _http_request._path = _http_request._url;
            }


        } 
        else if (_http_request._method == "POST")
        {
            _http_request._cgi = true;
            _http_request._path = _http_request._url;
        }
        else
        {
            // do nothing
        }

        // std::cout << _http_request._path << " " << _http_request._query_string << std::endl;
        path = _http_request._path;
        _http_request._path = WROOT;
        _http_request._path += path;
        // 访问的是根目录, 换成主页
        if (_http_request._path[_http_request._path.size() - 1] == '/')
        {
            _http_request._path += HOME_PAGE;
        }

        if (stat(_http_request._path.c_str(), &st) == 0)
        {
            // 访问的是目录
            if (S_ISDIR(st.st_mode))
            {
                // 不允许访问目录，请求目录，设置为这个目录的主页
                _http_request._path += "/";
                _http_request._path += HOME_PAGE;
                // 上面修改了路径，重新获取
                stat(_http_request._path.c_str(), &st);
            }

            // 访问可执行程序
            if ((st.st_mode & S_IXUSR) || (st.st_mode & S_IXGRP) || (st.st_mode & S_IXOTH))
            {
                std::cout << "访问程序:" << _http_request._path << std::endl;
                // 特殊处理
                _http_request._cgi = true;
            }

            _http_request._size = st.st_size;
        }
        else
        {
            // 资源不存在
            LOG(WARNING, _http_request._path + "NOT FOUND");
            code = NOT_FOUND;
            goto END;
        }

        // std::cout << "path:" << _http_request._path << std::endl;
        // suffix
        found = _http_request._path.rfind(".");
        if (found == std::string::npos)
        {
            _http_request._suffix = ".html";
        }
        else
        {
            _http_request._suffix = _http_request._path.substr(found);
        }

        // 是否cgi处理
        if (_http_request._cgi == true)
        {
            code = ProcessCgi();  // 执行目标程序，拿到结果：正文
        }
        else
        {
            // 目标网页一定存在
            // 返回要构建http响应
            code = ProcessNoCgi(); // 简单的网页处理，返回静态网页，只需要打开即可 
        }
END:
        // 构建错误响应
        BuildHttpResponseHelper();  // 状态行填充了，报头有了，空行有了，正文有了
    }

    void SendHttpResponse()
    {
        //std::cout << "status_line" << _http_response._status_line << std::endl;
        
        // for (auto it : _http_response._response_header)
        // {
        //     //std::cout << "status_head" << _http_response._status_line << std::endl;
        // }
        send(_sock, _http_response._status_line.c_str(), _http_response._status_line.size(), 0);
        for (auto it : _http_response._response_header)
        {
            send(_sock, it.c_str(), it.size(), 0);
        }
        send(_sock, _http_response._blank.c_str(), _http_response._blank.size(), 0);
        //std::string con = "hello world";
        if (_http_request._cgi)
        {
            // 内容在response里
            auto &response_body = _http_response.__response_body;
            size_t size = 0;
            size_t total = 0;
            const char *start = response_body.c_str();
            while ((total < response_body.size()) && (size = send(_sock, start + total, response_body.size() - total, 0)) > 0)
            {
                total += size;
            }
        }
        else
        {
            // 发送打开的文件
            sendfile(_sock, _http_response._fd, nullptr, _http_request._size);
            close(_http_response._fd);
        }
    }

    bool IsStop()
    {
        return _stop;
    }

    ~EndPoint()
    {}

private:
    int _sock;
    HttpRequest _http_request;
    HttpResponse _http_response;
    bool _stop;
};

class CallBack
{
public:
    CallBack()
    {}

    void operator()(int sock)
    {
        HandlerRequest(sock);
    }

    void HandlerRequest(int sock)
    {
        LOG(INFO, "handler request begin...");
        //int sock = *(int*)tmp_sock;
        //delete tmp_sock;

#ifdef DEBUG
        std::cout << "begin---------------------------" << sock << std::endl;
        char buff[4096];
        recv(sock, buff, sizeof(buff), 0);
        std::cout << buff;
        std::cout << "end-----------------------------" << sock << std::endl;
#else
        EndPoint *ep = new EndPoint(sock);
        ep->RecvHttpRequest();
        if (!ep->IsStop())
        {
            LOG(INFO, "recv success, begin build and send");
            ep->BuildHttpResponse();
            ep->SendHttpResponse();
        }
        else
        {
            LOG(INFO, "recv error, stop");
        }

        delete ep;
#endif
        LOG(INFO, "handle request end...");
        //return nullptr;
    }
    
    ~CallBack()
    {}
};